<?php
/**
 *
 *
 */
use Yz_Access_Interface as Access;

abstract class Yz_Controller_Abstract
    extends Yz_Class
    implements Yz_Controller_Interface
{

    /** Type of access
     *      NOTE: Constant because it can be useful to test it externally
     *  @const int
     */
    const ACCESS = Access::AC_PUBLIC;

    /** Reference to the module
     *  @var Yz_Module_Interface
     */
    protected $module;

    /** Reference to the router
     *  @var Yz_Router_Interface
     */
    protected $router;

    /** Reference to the view
     *  @var Yz_View_Interface
     */
    protected $view;

    /** the name of a view class which will be used
     *      NOTE: A protected variable (rather than const) because
     *      not designed to be externally available
     *  @var string
     */
    protected $viewClass = 'Yz_View_Default';




    /****************************
     *      Public methods      *
     ****************************/


    /** Model's constructor requires module
     *  @param Yz_Module_Interface $module
     */
    public function __construct( Yz_Module_Interface $module )
    {
        debug();
        $this->module = $module;
    }

    /**
     *  @param Yz_Router_Interface $router
     *  @return chainable
     */
    public function go( Yz_Router_Interface $router )
    {
        $this->router = $router;

        $this->checkAccess();

        if ( $router->isPOST() ) {
            return $this->doPOST();
        } else {
            return $this->doGET();
        }
    }

    /**
     *
     */
    public function getView()
    {
        $view =& $this->view;
        if (null === $view) {
            $view = new $this->viewClass();
        }
        return $view;
    }

    /**
     *
     * @return
     */
    protected function doGET()
    {
        throw new Yz_Exception_NotImplemented();
    }

    /**
     *
     */
    protected function doPOST()
    {
        throw new Yz_Exception_NotImplemented();
    }

    /**
     *
     */
    protected function getRequest()
    {
        return $this->router->getRequest();
    }

    /**
     *
     */
    protected function getResponse()
    {
        return $this->router->getResponse();
    }


    /**
     *  @param int $number
     *  @return string
     *  @throws Yz_Exception
     *  @todo throw a sensible exception
     */
    protected function fetchArgument($number)
    {
        debug();
        $args = $this->router->getArguments();
        try {
            $arg = $args[$number];
        } catch(Yz_Exception_IndexOutOfBounds $ex) {
            throw new Yz_Exception("Invalid number of arguments");
        }
        return $arg;
    }

    /**
     *  @return chainable
     *  @throw Yz_Access_Exception if the current user's access privileges do not match controller's
     */
    protected function checkAccess()
    {
        debug();
        switch (static::ACCESS) {
            case Access::AC_PUBLIC:
                break;
            case Access::AC_ADMIN:
                if (! $this->router->isAdmin()) {
                    $ex = new \Yz_Exception_ControllerNotFound();
                    throw $ex->withArgs(array(
                        'controllerName' => $this->router->getControllerSelector(),
                        'moduleName' => $this->router->getModuleSelector(),
                        ));
                }
                break;
            case Access::AC_VALID_USER:
                // not authenticated
                $user = Yz::selectLibrary("user");
                if ($user->isAuthenticated()) {
                    break;
                }
            case Access::AC_ROLE:
            default:
                throw new \Yz_Access_Exception();
        }
        return $this;
    }

}
